package main

import (
	"fmt"
	"gitcode.com/deyiyangyang/showen-lang/evl"
	"gitcode.com/deyiyangyang/showen-lang/typ"
)

// RunComprehensiveTests runs all the comprehensive tests
func RunComprehensiveTests() {
	// Create an environment
	env := typ.NewEnvironment()
	typ.DefineBuiltins(env)

	fmt.Println("===== Comprehensive Number Type Tests =====")
	
	// Test basic type creation
	fmt.Println("\n1. Basic Type Creation:")
	int1 := &typ.Integer{IntValue: 42}
	int2 := &typ.Integer{IntValue: -10}
	frac1 := &typ.Rational{Numerator: 3, Denominator: 4}
	frac2 := &typ.Rational{Numerator: 6, Denominator: 2} // Should be simplified to 3
	real1 := &typ.Real{RealValue: 3.14159}
	real2 := &typ.Real{RealValue: 2.71828}

	fmt.Printf("Integer 42: %s (Type: %d)\n", int1.String(), int1.Type())
	fmt.Printf("Integer -10: %s (Type: %d)\n", int2.String(), int2.Type())
	fmt.Printf("Rational 3/4: %s (Type: %d)\n", frac1.String(), frac1.Type())
	fmt.Printf("Rational 6/2: %s (Type: %d)\n", frac2.String(), frac2.Type()) // Should be simplified
	fmt.Printf("Real 3.14159: %s (Type: %d)\n", real1.String(), real1.Type())
	fmt.Printf("Real 2.71828: %s (Type: %d)\n", real2.String(), real2.Type())
	
	// Test basic operations
	fmt.Println("\n2. Basic Operations Test:")
	testOperation("+", []typ.Object{int1, frac1, real1}, env) // 42 + 3/4 + 3.14159
	testOperation("-", []typ.Object{int1, int2}, env)           // 42 - (-10)
	testOperation("*", []typ.Object{frac1, real1}, env)          // 3/4 * 3.14159
	testOperation("/", []typ.Object{int1, frac1}, env)           // 42 / 3/4
	
	// Test comparison operations
	fmt.Println("\n3. Comparison Operations Test:")
	testComparison(">", int1, int2, env)          // 42 > -10
	testComparison("<", frac1, real1, env)        // 3/4 < 3.14159
	testComparison("=", int1, &typ.Integer{IntValue: 42}, env)  // 42 == 42
	testComparison("=", &typ.Integer{IntValue: 3}, frac2, env)  // 3 == 6/2 (different types but same value)
	testComparison(">=", real1, &typ.Real{RealValue: 3.14}, env) // 3.14159 >= 3.14
	testComparison("<=", frac1, &typ.Rational{Numerator: 4, Denominator: 4}, env) // 3/4 <= 1
	
	// Test negative number handling
	fmt.Println("\n4. Negative Number Handling Test:")
	testOperation("*", []typ.Object{int2, int2}, env)          // -10 * -10
	testOperation("/", []typ.Object{int2, &typ.Integer{IntValue: 2}}, env) // -10 / 2
	testOperation("+", []typ.Object{int2, &typ.Integer{IntValue: 15}}, env) // -10 + 15
	
	// Test nested expressions
	fmt.Println("\n5. Nested Expressions Test:")
	nestedExpr := &typ.Pair{
		Car: &typ.Symbol{Value: "+"},
		Cdr: &typ.Pair{
			Car: &typ.Pair{
				Car: &typ.Symbol{Value: "*"},
				Cdr: &typ.Pair{
					Car: int1,
					Cdr: &typ.Pair{
						Car: &typ.Integer{IntValue: 2},
						Cdr: typ.Nil,
					},
				},
			},
			Cdr: &typ.Pair{
				Car: &typ.Pair{
					Car: &typ.Symbol{Value: "/"},
					Cdr: &typ.Pair{
						Car: &typ.Integer{IntValue: 100},
						Cdr: &typ.Pair{
							Car: &typ.Integer{IntValue: 5},
							Cdr: typ.Nil,
						},
					},
				},
				Cdr: typ.Nil,
			},
		},
	}
	result := evl.Eval(nestedExpr, env)
	fmt.Printf("(+ (* 42 2) (/ 100 5)) = %s\n", result.String()) // Should be 84 + 20 = 104
}

func testOperation(op string, args []typ.Object, env *typ.Environment) {
	// Build operation expression
	if len(args) == 0 {
		fmt.Printf("%s: no arguments\n", op)
		return
	}

	// Build expression tree
	expr := &typ.Pair{Car: &typ.Symbol{Value: op}}
	current := expr.Cdr

	for i, arg := range args {
		if i == 0 {
			expr.Cdr = &typ.Pair{Car: arg}
			current = expr.Cdr
		} else {
			current.(*typ.Pair).Cdr = &typ.Pair{Car: arg}
			current = current.(*typ.Pair).Cdr
		}
	}

	// Set the last CDR to nil
	if current != nil {
		current.(*typ.Pair).Cdr = typ.Nil
	}

	// Evaluate and print the result
	result := evl.Eval(expr, env)
	fmt.Printf("(%s %s) = %s\n", op, formatArgs(args), result.String())
}

func testComparison(op string, a, b typ.Object, env *typ.Environment) {
	// Build comparison expression
	expr := &typ.Pair{
		Car: &typ.Symbol{Value: op},
		Cdr: &typ.Pair{
			Car: a,
			Cdr: &typ.Pair{
				Car: b,
				Cdr: typ.Nil,
			},
		},
	}

	// Evaluate and print the result
	result := evl.Eval(expr, env)
	fmt.Printf("(%s %s %s) = %s\n", op, a.String(), b.String(), result.String())
}

func formatArgs(args []typ.Object) string {
	var result string
	for i, arg := range args {
		if i > 0 {
			result += " "
		}
		result += arg.String()
	}
	return result
}
